package cn.wjssl.domain.activity.service.quota;

import cn.wjssl.domain.activity.model.aggregate.CreateQuotaOrderAggregate;
import cn.wjssl.domain.activity.model.entity.*;
import cn.wjssl.domain.activity.model.valobj.OrderTradeTypeVO;
import cn.wjssl.domain.activity.repository.IActivityRepository;
import cn.wjssl.domain.activity.service.IRaffleActivityAccountQuotaService;
import cn.wjssl.domain.activity.service.quota.policy.ITradePolicy;
import cn.wjssl.domain.activity.service.quota.rule.IActionChain;
import cn.wjssl.domain.activity.service.quota.rule.factory.DefaultActivityChainFactory;
import cn.wjssl.types.enums.ResponseCode;
import cn.wjssl.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Map;

/**
 * 下sku单抽象类, 同样模版模式, 定义执行流程
 */
@Slf4j
public abstract class AbstractRaffleActivityAccountQuota extends RaffleActivityAccountQuotaSupport implements IRaffleActivityAccountQuotaService {

    @Resource
    private DefaultActivityChainFactory factory;

    @Resource
    private Map<String, ITradePolicy> tradePolicyMap;

    @Resource
    private IActivityRepository repository;

    /**
     * 创建 sku 账户充值订单，给用户增加抽奖次数
     * 抽象类, 模版模式, 定义下单流程
     * @param skuRechargeEntity 活动商品充值实体对象
     * @return 订单Id
     */
    @Override
    public UnpaidActivityOrderEntity createSkuRechargeOrder(SkuRechargeEntity skuRechargeEntity) {
        // 1. 参数校验
        String userId = skuRechargeEntity.getUserId();
        Long sku = skuRechargeEntity.getSku();
        String outBusinessNo = skuRechargeEntity.getOutBusinessNo();
        if (null == sku || StringUtils.isBlank(userId) || StringUtils.isBlank(outBusinessNo)) {
            throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
        }

        // 2. 通过 support 类, 查询基础信息, 因为继承了support类, 所以直接写方法名即可
        // 2.1 通过sku查询活动SKU信息
        ActivitySkuEntity activitySkuEntity = queryActivitySku(sku);
        // 2.2 查询活动信息
        ActivityEntity activityEntity = queryRaffleActivityByActivityId(activitySkuEntity.getActivityId());
        // 2.3 查询次数信息（用户在活动上可参与的次数）
        ActivityCountEntity activityCountEntity = queryRaffleActivityCountByActivityCountId(activitySkuEntity.getActivityCountId());

        // 3. 校验积分账户余额, 如果不满足sku单价格, 直接就返回
        if (OrderTradeTypeVO.credit_pay_trade.equals(skuRechargeEntity.getOrderTradeType())){
            BigDecimal availableAmount = repository.queryUserCreditAccountAmount(userId);
            if (availableAmount.compareTo(activitySkuEntity.getProductAmount()) < 0) {
                throw new AppException(ResponseCode.USER_CREDIT_ACCOUNT_NO_AVAILABLE_AMOUNT.getCode(), ResponseCode.USER_CREDIT_ACCOUNT_NO_AVAILABLE_AMOUNT.getInfo());
            }
        }

        // 4. 活动动作规则校验, 通过工厂, 获取责任链
        IActionChain actionChain = factory.openActionChain();
        actionChain.action(activitySkuEntity, activityEntity, activityCountEntity);

        // 5. 构建订单聚合对象
        // 其中, 如果是需要支付的sku商品, 从support类查询的 activitySkuEntity 中获取
        CreateQuotaOrderAggregate orderAggregate = buildOrderAggregate(skuRechargeEntity, activitySkuEntity, activityEntity, activityCountEntity);

        /**
         * 6. 交易策略
         * 【积分兑换，支付类订单】- credit_pay_trade
         * 【返利无支付交易订单，直接充值到账】- rebate_no_pay_trade
         * 根据sku订单类型, 调用对应的处理器
         */
        ITradePolicy tradePolicy = tradePolicyMap.get(skuRechargeEntity.getOrderTradeType().getCode());
        tradePolicy.trade(orderAggregate);

        // 7. 返回未支付sku单实体类
        ActivityOrderEntity activityOrderEntity = orderAggregate.getActivityOrderEntity();
        return UnpaidActivityOrderEntity.builder()
                .userId(userId)
                .orderId(activityOrderEntity.getOrderId())
                .outBusinessNo(activityOrderEntity.getOutBusinessNo())
                .payAmount(activityOrderEntity.getPayAmount())
                .build();
    }

    // 创建订单聚合对象, 用于更新数据库
    protected abstract CreateQuotaOrderAggregate buildOrderAggregate(SkuRechargeEntity skuRechargeEntity, ActivitySkuEntity activitySkuEntity, ActivityEntity activityEntity, ActivityCountEntity activityCountEntity);

}
